package com.meshnetics.gb.type.impl.zcl;

import com.meshnetics.gb.type.impl.ByteUtils;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.Arrays;

/**
 * WRITE DESCRIPTION HERE
 *
 * @author AKovylin
 */
public abstract class FixedBytesDataValue extends AbstractDataValue {
    protected final byte[] value;

    // write comment - constructor only for extension, no checks or cloning performed!
    // (to avoid double checks/cloning, checks/cloning must be done by type on deserialization)
    protected FixedBytesDataValue(FixedBytesDataType type, byte[] value) {
        super(type);
        this.value = value;
    }

    public int getDataSize() {
        return value.length;
    }

    public byte[] bytesValue() {
        return (byte[]) value.clone();
    }

    public void serialize(ByteBuffer bb) {
        ByteUtils.ensureNotNull(bb);
        ByteUtils.writeToByteBuffer(bb, bytesValue());
    }

    public Object toObject() {
        Object res;
        switch (value.length) {
            case 1: // as Byte
                res = toByte();
                break;
            case 2: // as Short
                res = toShort();
                break;
            case 3: // as Integer
            case 4: // as Integer
                res = toInteger();
                break;
            case 5: // as Long
            case 6: // as Long
            case 7: // as Long
            case 8: // as Long TODO: 1) Commented BY Pavlov Dmitry because long type could not include all values in unsinged
                      // version of data type 2) Uncommented by mmarkov back - stack could not start
                res = toLong();
                break;
            default:
                res = isUnsigned() ? new BigInteger(1, value) : new BigInteger(value);
        }
        return res;
    }

    protected abstract boolean isUnsigned();

    protected Long toLong() {
        long res = value[0];
        for (int i = 1; i < value.length; i++) {
            res = (res << 8) + (value[i] & 0xff);
        }
        return new Long(res);
    }

    protected Integer toInteger() {
        int res = value[0];
        for (int i = 1; i < value.length; i++) {
            res = (res << 8) + (value[i] & 0xff);
        }
        return new Integer(res);
    }

    protected Short toShort() {
        short s = (short) (value[0] << 8);
        s += value[1] & 0xff;
        return new Short(s);
    }

    protected Byte toByte() {
        return new Byte(value[0]);
    }

    public boolean equals(Object obj) {
        return super.equals(obj) && this.getClass() == obj.getClass() &&
                Arrays.equals(this.value, ((FixedBytesDataValue) obj).value);
    }

    public int hashCode() {
        return super.hashCode() + 29 * Arrays.hashCode(value);
    }
}
